﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ttlang.node;

//拼字符串模组
namespace ttlang.builder
{
    //为啥是cplus，我们产生的代码风格是以c为基础，cpp为辅助。
    //所以只取一个plus
    public class builder_c : IBuilder
    {
        public string Name => "CPlus SourceCode";
        public BuildTarget type => BuildTarget.C;
        public Result BuildNode(Node node, out string result)
        {
            if (node is Node_Expr expr)
            {
                StringBuilder sb = new StringBuilder();
                BuildNode_Expr(sb, expr);
                result = sb.ToString();
                return null;
            }
            else if (node is Node_CodeAtom code)
            {
                StringBuilder sb = new StringBuilder();
                BuildNode_Code(sb, code, 0);
                result = sb.ToString();
                return null;
            }

            throw new Exception("not support node:" + node.type);
        }

        public Result BuildModule(semantic.Module module, string outputpath, string temppath, ProjectOption.ProjectType outputType, bool autoBuild,Newtonsoft.Json.Linq.JObject json)
        {
            Result result = new Result(outputpath, temppath);


            return result;
            
        }
        static void BuildNode_Expr(StringBuilder sb, Node_Expr expr)
        {
            switch (expr.type)
            {
                case NodeType.EXPR_CONTAINER:
                    {
                        var v = expr as Node_Expr_Container;
                        sb.Append(v.container.fullname);
                    }
                    break;
                case NodeType.EXPR_NUMBER:
                    {
                        var v = expr as Node_Expr_Number;
                        if (v == null)
                            throw new Exception("error type");
                        sb.Append(v.srcnumber);
                    }
                    break;
                case NodeType.EXPR_STRING:
                    {
                        var v = expr as Node_Expr_String;
                        if (v == null)
                            throw new Exception("error type");
                        sb.Append(v.value);

                    }
                    break;

                case NodeType.EXPR_MATH:
                    {
                        var v = expr as Node_Expr_Math;
                        if (v == null)
                            throw new Exception("error type");
                        sb.Append("(");

                        //这里暂时简单处理，实际build的时候可能会出现数学表达式变函数调用的情况
                        BuildNode_Expr(sb, v.left);
                        sb.Append(" " + v.token + " ");
                        BuildNode_Expr(sb, v.right);

                        sb.Append(")");

                    }
                    break;
                case NodeType.EXPR_TEMPVAR:
                    {
                        var v = expr as Node_Expr_TempVar;
                        if (v == null)
                            throw new Exception("error type");
                        sb.Append(v.id.id);
                    }
                    break;
                case NodeType.EXPR_METHOD:
                    {
                        var v = expr as Node_Expr_Method;
                        if (v == null)
                            throw new Exception("error type");
                        //这也是个简单的处理，以后要考虑method的namespace
                        sb.Append(v.method.name);
                    }
                    break;
                case NodeType.EXPR_3OP:
                    {

                        var v = expr as Node_Expr_Math3Op;
                        if (v == null)
                            throw new Exception("error type");

                        sb.Append("(");

                        BuildNode_Expr(sb, v.condition);

                        sb.Append("?");

                        BuildNode_Expr(sb, v.vTrue);

                        sb.Append(":");

                        BuildNode_Expr(sb, v.vFalse);


                        sb.Append(")");
                    }
                    break;
                case NodeType.EXPR_MEMBER:
                    {
                        var v = expr as Node_Expr_Member;
                        if (v == null)
                            throw new Exception("error type");
                        BuildNode_Expr(sb, v.left);
                        sb.Append(".");
                        sb.Append(v.member.name);
                    }
                    break;

                case NodeType.EXPR_CALL:
                    {
                        var v = expr as Node_Expr_Call;
                        if (v == null)
                            throw new Exception("error type");
                        BuildNode_Expr(sb, v.function);
                        sb.Append("(");
                        for (var i = 0; i < v.args?.Count; i++)
                        {
                            BuildNode_Expr(sb, v.args[i]);
                            if (i < v.args.Count - 1)
                                sb.Append(",");
                        }
                        sb.Append(")");
                    }
                    break;
                default:
                    throw new NotImplementedException();

            }

        }
        static void BuildNode_Code(StringBuilder sb, Node_CodeAtom code, int deep, bool skiplineend = false)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
                space += "    ";
            switch (code.type)
            {
                case NodeType.CODE_BLOCK:
                    {
                        var block = code as Node_CodeBlock;
                        if (block == null)
                            throw new Exception("error type");
                        sb.AppendLine(space + "{");
                        for (var i = 0; i < block.items.Count; i++)
                        {
                            BuildNode_Code(sb, block.items[i], deep + 1);
                        }
                        sb.AppendLine(space + "}");
                    }
                    break;
                case NodeType.CODE_COMMENT:
                    {
                        var v = code as Node_Code_Comment;
                        if (v == null)
                            throw new Exception("error type");
                        sb.Append(space + v.comment);
                        if (!skiplineend || v.commonttype == CommentType.SINGLELINE)
                            sb.AppendLine();
                        break;
                    }
                case NodeType.CODE_VALUEDEF:
                    {
                        var v = code as Node_Code_ValueDef;
                        if (v == null)
                            throw new Exception("error type");
                        BuildNode_Code_ValueDef(sb, v, deep, skiplineend);
                        break;
                    }
                case NodeType.CODE_SELFOP:
                    {
                        var v = code as Node_Code_SelfOf;
                        if (v == null)
                            throw new Exception("error type");
                        BuildNode_Code_SelfOp(sb, v, deep, skiplineend);
                        break;
                    }
                case NodeType.CODE_FOR:
                    {
                        var v = code as Node_Code_For;
                        if (v == null)
                            throw new Exception("error type");
                        BuildNode_Code_For(sb, v, deep, skiplineend);
                        break;
                    }
                case NodeType.CODE_CALL:
                    {
                        var v = code as Node_Code_Call;
                        if (v == null)
                            throw new Exception("error type");
                        BuildNode_Code_Call(sb, v, deep, skiplineend);
                        break;
                    }
                default:
                    throw new NotImplementedException();
            }
        }
        static void BuildNode_Code_ValueDef(StringBuilder sb, Node_Code_ValueDef code, int deep, bool skiplineend)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
                space += "    ";
            sb.Append(space + code.deftype.fullname + " " + code.id);
            if (code.initvlaue != null)
            {
                sb.Append(" = ");
                BuildNode_Expr(sb, code.initvlaue);
            }
            if (!skiplineend)
                sb.AppendLine(";");

        }
        static void BuildNode_Code_SelfOp(StringBuilder sb, Node_Code_SelfOf code, int deep, bool skiplineend)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
                space += "    ";
            {
                sb.Append(space);
                BuildNode_Expr(sb, code.id_expr);
            }
            sb.Append(" " + code.token + " ");
            if (code.rightvalue != null)
            {
                BuildNode_Expr(sb, code.rightvalue);
            }
            if (!skiplineend)
                sb.AppendLine(";");
        }
        static void BuildNode_Code_For(StringBuilder sb, Node_Code_For code, int deep, bool skiplineend)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
                space += "    ";

            sb.Append(space + "for(");
            if (code.elem_init != null)
                BuildNode_Code(sb, code.elem_init, 0, true);
            sb.Append(";");
            if (code.elem_cond != null)
                BuildNode_Expr(sb, code.elem_cond);
            sb.Append(";");
            if (code.elem_step != null)
                BuildNode_Code(sb, code.elem_step, 0, true);
            sb.AppendLine(")");

            //body
            BuildNode_Code(sb, code.body, deep, false);
        }
        static void BuildNode_Code_Call(StringBuilder sb, Node_Code_Call code, int deep, bool skiplineend)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
                space += "    ";

            sb.Append(space);
            BuildNode_Expr(sb, code.call_expr);
            if (!skiplineend)
                sb.AppendLine(";");
        }
    }
}
